<!DOCTYPE html>
<!--这个就是把最原始版本的格式改好了，注释翻译为中文-->
<meta charset="utf-8">
<title>原 - 中文</title>
<style type="text/css">
	.node {
		cursor: pointer;
	}
	
	.overlay {
		background-color: #EEE;
	}
	
	.node circle {
		fill: #fff;
		stroke: steelblue;
		stroke-width: 1.5px;
	}
	
	.node text {
		font-size: 10px;
		font-family: sans-serif;
	}
	
	.link {
		fill: none;
		stroke: #ccc;
		stroke-width: 1.5px;
	}
	
	.templink {
		fill: none;
		stroke: red;
		stroke-width: 3px;
	}
	
	.ghostCircle.show {
		display: block;
	}
	
	.ghostCircle,
	.activeDrag .ghostCircle {
		display: none;
	}
</style>
<!--<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>-->
<!--<script src="http://d3js.org/d3.v3.min.js"></script>-->
<script src="d3/jquery-1.10.2.min.js"></script>
<script src="d3/d3.v3.min.js"></script>

<body>
	<div id="tree-container"></div>
</body>

<script>
// Get JSON data
treeJSON = d3.json("./flare.json", function(error, treeData) {
	// 计算总节点数，最大标签长度
	var totalNodes = 0;
	var maxLabelLength = 0;
	// 变量拖/下降
	var selectedNode = null;
	var draggingNode = null;
	// 平移变量
	var panSpeed = 200;
	var panBoundary = 20; // 拖动时，在距边缘20px内将平移。
	// 混杂。变量
	var i = 0;
	var duration = 750;
	var root;

	// 图的大小
	var viewerWidth = $(document).width();
	var viewerHeight = $(document).height();

	var tree = d3.layout.tree()
		.size([viewerHeight, viewerWidth]);

	// 定义一个d3对角投影供以后节点路径使用。
	var diagonal = d3.svg.diagonal()
		.projection(function(d) {
			return [d.y, d.x];
		});

	// 通过遍历所有节点来执行某些设置的递归帮助函数

	function visit(parent, visitFn, childrenFn) {
		if(!parent) return;

		visitFn(parent);

		var children = childrenFn(parent);
		if(children) {
			var count = children.length;
			for(var i = 0; i < count; i++) {
				visit(children[i], visitFn, childrenFn);
			}
		}
	}

	// 调用访问函数来建立maxLabelLength
	visit(treeData, function(d) {
		totalNodes++;
		maxLabelLength = Math.max(d.name.length, maxLabelLength);

	}, function(d) {
		return d.children && d.children.length > 0 ? d.children : null;
	});

	// 根据节点名对树进行排序

	function sortTree() {
		tree.sort(function(a, b) {
			return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
		});
	}
	// 开始时对树进行排序，以防JSON不是按排序顺序排列的。
	sortTree();

	// TODO: Pan功能，可以更好的实现。

	function pan(domNode, direction) {
		var speed = panSpeed;
		if(panTimer) {
			clearTimeout(panTimer);
			translateCoords = d3.transform(svgGroup.attr("transform"));
			if(direction == 'left' || direction == 'right') {
				translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed;
				translateY = translateCoords.translate[1];
			} else if(direction == 'up' || direction == 'down') {
				translateX = translateCoords.translate[0];
				translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed;
			}
			scaleX = translateCoords.scale[0];
			scaleY = translateCoords.scale[1];
			scale = zoomListener.scale();
			svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
			d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
			zoomListener.scale(zoomListener.scale());
			zoomListener.translate([translateX, translateY]);
			panTimer = setTimeout(function() {
				pan(domNode, speed, direction);
			}, 50);
		}
	}

	// 定义可缩放树的缩放功能

	function zoom() {
		svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
	}

	// 定义zoomListener，它在scaleExtents中约束的“zoom”事件上调用zoom函数
	var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);

	function initiateDrag(d, domNode) {
		draggingNode = d;
		d3.select(domNode).select('.ghostCircle').attr('pointer-events', 'none');
		d3.selectAll('.ghostCircle').attr('class', 'ghostCircle show');
		d3.select(domNode).attr('class', 'node activeDrag');

		svgGroup.selectAll("g.node").sort(function(a, b) { // 选择父类并对路径排序
			if(a.id != draggingNode.id) return 1; // a不是盘旋的元素，发送“a”到后面
			else return -1; // a是盘旋的元素，把“a”放在前面
		});
		// 如果节点有子节点，则删除链接和节点
		if(nodes.length > 1) {
			// 删除链接路径
			links = tree.links(nodes);
			nodePaths = svgGroup.selectAll("path.link")
				.data(links, function(d) {
					return d.target.id;
				}).remove();
			// 删除子节点
			nodesExit = svgGroup.selectAll("g.node")
				.data(nodes, function(d) {
					return d.id;
				}).filter(function(d, i) {
					if(d.id == draggingNode.id) {
						return false;
					}
					return true;
				}).remove();
		}

		// 删除父链接
		parentLink = tree.links(tree.nodes(draggingNode.parent));
		svgGroup.selectAll('path.link').filter(function(d, i) {
			if(d.target.id == draggingNode.id) {
				return true;
			}
			return false;
		}).remove();

		dragStarted = null;
	}

	// 定义baseSvg，附加一个用于样式化的类和zoomListener
	var baseSvg = d3.select("#tree-container").append("svg")
		.attr("width", viewerWidth)
		.attr("height", viewerHeight)
		.attr("class", "overlay")
		.call(zoomListener);

	// 为节点的拖放行为定义拖放侦听器。
	dragListener = d3.behavior.drag()
		.on("dragstart", function(d) {
			if(d == root) {
				return;
			}
			dragStarted = true;
			nodes = tree.nodes(d);
			d3.event.sourceEvent.stopPropagation();
			// 一定要在被拖动的节点上禁止mouseover事件。否则，它将吸收mouseover事件，而底层节点不会检测它 d3.select(this).attr('pointer-events', 'none');
		})
		.on("drag", function(d) {
			if(d == root) {
				return;
			}
			if(dragStarted) {
				domNode = this;
				initiateDrag(d, domNode);
			}

			// 获取与svg容器相关的mouseEvent的coords，以便进行平移
			relCoords = d3.mouse($('svg').get(0));
			if(relCoords[0] < panBoundary) {
				panTimer = true;
				pan(this, 'left');
			} else if(relCoords[0] > ($('svg').width() - panBoundary)) {

				panTimer = true;
				pan(this, 'right');
			} else if(relCoords[1] < panBoundary) {
				panTimer = true;
				pan(this, 'up');
			} else if(relCoords[1] > ($('svg').height() - panBoundary)) {
				panTimer = true;
				pan(this, 'down');
			} else {
				try {
					clearTimeout(panTimer);
				} catch(e) {

				}
			}

			d.x0 += d3.event.dy;
			d.y0 += d3.event.dx;
			var node = d3.select(this);
			node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
			updateTempConnector();
		}).on("dragend", function(d) {
			if(d == root) {
				return;
			}
			domNode = this;
			if(selectedNode) {
				// 现在从父元素中删除元素，并将其插入到新元素的子元素中
				var index = draggingNode.parent.children.indexOf(draggingNode);
				if(index > -1) {
					draggingNode.parent.children.splice(index, 1);
				}
				if(typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
					if(typeof selectedNode.children !== 'undefined') {
						selectedNode.children.push(draggingNode);
					} else {
						selectedNode._children.push(draggingNode);
					}
				} else {
					selectedNode.children = [];
					selectedNode.children.push(draggingNode);
				}
				// 确保要添加到的节点已展开，以便用户可以看到添加的节点已被正确移动
				expand(selectedNode);
				sortTree();
				endDrag();
			} else {
				endDrag();
			}
			console.log(root)
		});

	function endDrag() {
		selectedNode = null;
		d3.selectAll('.ghostCircle').attr('class', 'ghostCircle');
		d3.select(domNode).attr('class', 'node');
		// 现在恢复鼠标悬停事件，否则我们将无法拖动第二次
		d3.select(domNode).select('.ghostCircle').attr('pointer-events', '');
		updateTempConnector();
		if(draggingNode !== null) {
			update(root);
			centerNode(draggingNode);
			draggingNode = null;
		}

	}

	// 用于折叠和展开节点的辅助函数。

	function collapse(d) {
		if(d.children) {
			d._children = d.children;
			d._children.forEach(collapse);
			d.children = null;
		}
	}

	function expand(d) {
		if(d._children) {
			d.children = d._children;
			d.children.forEach(expand);
			d._children = null;
		}
	}

	var overCircle = function(d) {
		selectedNode = d;
		updateTempConnector();
	};
	var outCircle = function(d) {
		selectedNode = null;
		updateTempConnector();
	};

	// 函数更新指示拖动从属关系的临时连接器
	var updateTempConnector = function() {
		var data = [];
		if(draggingNode !== null && selectedNode !== null) {
			// 必须翻转源坐标，因为我们对原始树中的现有连接器做了这个
			data = [{
				source: {
					x: selectedNode.y0,
					y: selectedNode.x0
				},
				target: {
					x: draggingNode.y0,
					y: draggingNode.x0
				}
			}];
		}
		var link = svgGroup.selectAll(".templink").data(data);

		link.enter().append("path")
			.attr("class", "templink")
			.attr("d", d3.svg.diagonal())
			.attr('pointer-events', 'none');

		link.attr("d", d3.svg.diagonal());

		link.exit().remove();
	};

	// 功能到中心节点时，点击/删除，使节点不会丢失时崩溃/移动大量的孩子。

	function centerNode(source) {
		scale = zoomListener.scale();
		x = -source.y0;
		y = -source.x0;
		x = x * scale + viewerWidth / 2;
		y = y * scale + viewerHeight / 2;
		d3.select('g').transition()
			.duration(duration)
			.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
		zoomListener.scale(scale);
		zoomListener.translate([x, y]);
	}

	// 切换孩子函数

	function toggleChildren(d) {
		if(d.children) {
			d._children = d.children;
			d.children = null;
		} else if(d._children) {
			d.children = d._children;
			d._children = null;
		}
		return d;
	}

	// 切换儿童点击。

	function click(d) {
		if(d3.event.defaultPrevented) return; // 单击镇压
		d = toggleChildren(d);
		update(d);
		centerNode(d);
	}

	function update(source) {
		// 计算新的高度，函数计算根节点的所有子节点并相应地设置树的高度。
		// 这可以防止在可见新节点时布局看起来被压扁，或者在删除节点时布局看起来很稀疏
		// 这使得布局更加一致。
		var levelWidth = [1];
		var childCount = function(level, n) {

			if(n.children && n.children.length > 0) {
				if(levelWidth.length <= level + 1) levelWidth.push(0);

				levelWidth[level + 1] += n.children.length;
				n.children.forEach(function(d) {
					childCount(level + 1, d);
				});
			}
		};
		childCount(0, root);
		var newHeight = d3.max(levelWidth) * 25; // 每行25像素
		tree = tree.size([newHeight, viewerWidth]);

		// 计算新的树布局。
		var nodes = tree.nodes(root).reverse(),
			links = tree.links(nodes);

		// 根据maxLabelLength设置级别之间的宽度。
		nodes.forEach(function(d) {
			d.y = (d.depth * (maxLabelLength * 10)); //maxLabelLength * 10px
			// 或者，保持一个固定的规模，一个人可以设置一个固定的深度每水平
			// 通过注释下面的行来规范化固定深度
			// d.y = (d.depth * 500); //500px per level.
		});

		// 更新节点…
		node = svgGroup.selectAll("g.node")
			.data(nodes, function(d) {
				return d.id || (d.id = ++i);
			});

		// 在父节点之前的位置输入任何新节点。
		var nodeEnter = node.enter().append("g")
			.call(dragListener)
			.attr("class", "node")
			.attr("transform", function(d) {
				return "translate(" + source.y0 + "," + source.x0 + ")";
			})
			.on('click', click);

		nodeEnter.append("circle")
			.attr('class', 'nodeCircle')
			.attr("r", 0)
			.style("fill", function(d) {
				return d._children ? "lightsteelblue" : "#fff";
			});

		nodeEnter.append("text")
			.attr("x", function(d) {
				return d.children || d._children ? -10 : 10;
			})
			.attr("dy", ".35em")
			.attr('class', 'nodeText')
			.attr("text-anchor", function(d) {
				return d.children || d._children ? "end" : "start";
			})
			.text(function(d) {
				return d.name;
			})
			.style("fill-opacity", 0);

		// 幻影节点，让我们把鼠标移到它周围的半径内
		nodeEnter.append("circle")
			.attr('class', 'ghostCircle')
			.attr("r", 30)
			.attr("opacity", 0.2) // 将其更改为0以隐藏目标区域
			.style("fill", "red")
			.attr('pointer-events', 'mouseover')
			.on("mouseover", function(node) {
				overCircle(node);
			})
			.on("mouseout", function(node) {
				outCircle(node);
			});

		// 更新文本以反映node是否有子节点。
		node.select('text')
			.attr("x", function(d) {
				return d.children || d._children ? -10 : 10;
			})
			.attr("text-anchor", function(d) {
				return d.children || d._children ? "end" : "start";
			})
			.text(function(d) {
				return d.name;
			});

		// 根据是否有子元素和是否折叠来改变圆的填充
		node.select("circle.nodeCircle")
			.attr("r", 4.5)
			.style("fill", function(d) {
				return d._children ? "lightsteelblue" : "#fff";
			});

		// 将节点转换到它们的新位置。
		var nodeUpdate = node.transition()
			.duration(duration)
			.attr("transform", function(d) {
				return "translate(" + d.y + "," + d.x + ")";
			});

		// 淡入文本
		nodeUpdate.select("text")
			.style("fill-opacity", 1);

		// 将现有节点转换到父节点的新位置。
		var nodeExit = node.exit().transition()
			.duration(duration)
			.attr("transform", function(d) {
				return "translate(" + source.y + "," + source.x + ")";
			})
			.remove();

		nodeExit.select("circle")
			.attr("r", 0);

		nodeExit.select("text")
			.style("fill-opacity", 0);

		// Update the links…
		var link = svgGroup.selectAll("path.link")
			.data(links, function(d) {
				return d.target.id;
			});

		// 在父节点之前的位置输入任何新链接。
		link.enter().insert("path", "g")
			.attr("class", "link")
			.attr("d", function(d) {
				var o = {
					x: source.x0,
					y: source.y0
				};
				return diagonal({
					source: o,
					target: o
				});
			});

		// 过渡链接到他们的新位置。
		link.transition()
			.duration(duration)
			.attr("d", diagonal);

		// 将现有节点转换到父节点的新位置。
		link.exit().transition()
			.duration(duration)
			.attr("d", function(d) {
				var o = {
					x: source.x,
					y: source.y
				};
				return diagonal({
					source: o,
					target: o
				});
			})
			.remove();

		// 把旧的职位藏起来，等待交接。
		nodes.forEach(function(d) {
			d.x0 = d.x;
			d.y0 = d.y;
		});
	}

	// 附加一个包含所有节点的组，缩放监听器可以对其进行操作。
	var svgGroup = baseSvg.append("g");

	// 定义根
	root = treeData;
	root.x0 = viewerHeight / 2;
	root.y0 = 0;

	// 初始布局树，并以根节点为中心。
	update(root);
	centerNode(root);
});
</script>
</html>